#ifndef SIM_H
#define SIM_H

#include "CaffProc/emulator_interface.h"
#include "uarch/enums.h"
#include "uarch/mreq.h"
#include "uarch/node.h"
#include "repeater.h"
#include "settings.h"
//#include "CaffProc/proc_settings.h"
//#include "CaffCache/cache_settings.h"
//#include "CaffNet/net_settings.h"
//#include "CaffMC/mem_settings.h"
#include "stat.h"
#include "stdarg.h"
#include "types.h"
#include "stat_engine.h"
#include "sim_stat_manager.h"
#include "CaffCache/MOESI_directory.h"
#include "iris/topology/topology.h"
#include "uarch/node_to_iris_module.h"
#include "uarch/node_terminal.h"

//#ifndef USE_IRIS
//#include "CaffNet/noc.h"
//#endif

//#define Global_Clock Sim.global_clock
#define Global_Clock (manifold::kernel::Manifold::NowTicks())

class Thread;
class Node;
class processor;
class Hash_table;
class L1_cache;
class L2_cache;
class Directory;
class Memory_controller;
class Mesh_interface;
class Mesh;
class Router;
class Sim_settings;
class Sim_stat_manager;

// Fatal errors.
void fatal_error (const char *fmt, ...) __attribute__ ((noreturn));

class Simulator {
public:

    /** Simulator settings.  */
    Sim_settings settings;
//    Proc_settings proc_settings;
//    Cache_settings cache_settings;
//    Net_settings net_settings;
//    Mem_settings mem_settings;

#if !SIM_WITH_ZESTO
    Emulator_interface emul;
#endif

    /** Global clock.  */
    timestamp_t global_clock;

    /** Nodes and mesh.  */
    Node **Nd;
//#ifndef USE_IRIS
//    Noc *NOC;
//#endif
    Topology* tp;

    /** Live lock array.  */
    counter_t * livelock_arr;

    /** Constructor/Destructor, etc.  */
    Simulator ();
    ~Simulator ();

    void Manifold_instantiate ();

    /** Run/Fini for simulator.  */
    void run (void);
    bool tick_tock (void);
    void flush (void);
    void fini (void);
    void dump_stats (void);

    /** Livelock management.  */
    void livelock_check (void);
    void livelock_detected (int core, int request);

    void thread_count ();

    /** Connect a mesh interface to a router.  */
    void connect_NI_to_router (Mesh_interface *ni, Router *r);

    /** Coherence.  */
    int get_home_node_id (paddr_t addr);
    int get_local_replica_node_id (int origin_node_id, paddr_t addr);
    int get_memory_controller_id (paddr_t addr);
    module_t get_querier_module_index (module_t module_index);
    module_t get_directory_module_index (module_t module_index);
    //TODO: module_t module_index[INVALID_M];

    /** Repeater Events.  */
    std::multimap<timestamp_t, Repeater_event *> repeater_event_queue;
    void add_repeater_event (Repeater_event *re);
    void process_repeater_event_queue ();
    void collect_qset_stat ();

    /** Accessor functions */
    Mesh_interface *get_NI (int node);
    processor *get_PR (int node);
    Hash_table *get_L1 (int node);
    Hash_table *get_L2 (int node);
    Hash_table *get_L3 (int node);
    Memory_controller *get_MC (int node);

    /** Debug.  */
    void dump_processors (void);
	void dump_outstanding_requests (int nodeID);
    void dump_cache_block (int nodeID, paddr_t addr);

    /** Stats.  */
    Simulator_stat_engine *stats;
    Sim_stat_manager *stat_manager;

};

class Simulator_stat_engine : public Stat_engine
{
public:
    Simulator_stat_engine ();
    ~Simulator_stat_engine ();

    Persistent_stat<counter_t> cycle_count;
    Persistent_histogram_stat<counter_t> qset_1lvl_hist;
    Sampled_stat<timestamp_t> qset_1lvl_sampled_timestamp;
    Sampled_stat<int> qset_1lvl_sampled_count;
    Sampled_stat<double> qset_1lvl_sampled_latency;
    Sampled_stat<double> qset_2lvl_sampled_latency;
    Sampled_stat<int> qset_1lvl_sampled_latency_cnt;
    Sampled_stat<int> qset_2lvl_sampled_latency_cnt;
    Persistent_histogram_stat<counter_t> active_threads_hist;

	void global_stat_merge(Stat_engine * e);
	void clear_stats();
    void print_stats (ostream & out);
    void start_warmup();
    void end_warmup();
    void save_samples();

};

#endif

